﻿/*
 * jGraduate 0.4
 *
 * jQuery Plugin for a gradient picker
 *
 * Copyright (c) 2010 Jeff Schiller
 * http://blog.codedread.com/
 * Copyright (c) 2010 Alexis Deveria
 * http://a.deveria.com/
 *
 * Apache 2 License

jGraduate( options, okCallback, cancelCallback )

where options is an object literal:
  {
    window: { title: "Pick the start color and opacity for the gradient" },
    images: { clientPath: "images/" },
    paint: a Paint object,
    newstop: String of value "same", "inverse", "black" or "white" 
         OR object with one or both values {color: #Hex color, opac: number 0-1}
  }
 
- the Paint object is:
  Paint {
    type: String, // one of "none", "solidColor", "linearGradient", "radialGradient"
    alpha: Number representing opacity (0-100),
    solidColor: String representing #RRGGBB hex of color,
    linearGradient: object of interface SVGLinearGradientElement,
    radialGradient: object of interface SVGRadialGradientElement,
  }

$.jGraduate.Paint() -> constructs a 'none' color
$.jGraduate.Paint({copy: o}) -> creates a copy of the paint o
$.jGraduate.Paint({hex: "#rrggbb"}) -> creates a solid color paint with hex = "#rrggbb"
$.jGraduate.Paint({linearGradient: o, a: 50}) -> creates a linear gradient paint with opacity=0.5
$.jGraduate.Paint({radialGradient: o, a: 7}) -> creates a radial gradient paint with opacity=0.07
$.jGraduate.Paint({hex: "#rrggbb", linearGradient: o}) -> throws an exception?

- picker accepts the following object as input:
  {
    okCallback: function to call when Ok is pressed
    cancelCallback: function to call when Cancel is pressed
    paint: object describing the paint to display initially, if not set, then default to opaque white
  }

- okCallback receives a Paint object

 *
 */

(function () {

  var ns = { svg: 'http://www.w3.org/2000/svg', xlink: 'http://www.w3.org/1999/xlink' };
  if (!window.console) {
    window.console = new function () {
      this.log = function (str) { };
      this.dir = function (str) { };
    };
  }

  $.jGraduate = {
    Paint:
      function (opt) {
        var options = opt || {};
        this.alpha = isNaN(options.alpha) ? 100 : options.alpha;
        // copy paint object
        if (options.copy) {
          this.type = options.copy.type;
          this.alpha = options.copy.alpha;
          this.solidColor = null;
          this.linearGradient = null;
          this.radialGradient = null;

          switch (this.type) {
            case "none":
              break;
            case "solidColor":
              this.solidColor = options.copy.solidColor;
              break;
            case "linearGradient":
              this.linearGradient = options.copy.linearGradient.cloneNode(true);
              break;
            case "radialGradient":
              this.radialGradient = options.copy.radialGradient.cloneNode(true);
              break;
          }
        }
        // create linear gradient paint
        else if (options.linearGradient) {
          this.type = "linearGradient";
          this.solidColor = null;
          this.radialGradient = null;
          this.linearGradient = options.linearGradient.cloneNode(true);
        }
        // create linear gradient paint
        else if (options.radialGradient) {
          this.type = "radialGradient";
          this.solidColor = null;
          this.linearGradient = null;
          this.radialGradient = options.radialGradient.cloneNode(true);
        }
        // create solid color paint
        else if (options.solidColor) {
          this.type = "solidColor";
          this.solidColor = options.solidColor;
        }
        // create empty paint
        else {
          this.type = "none";
          this.solidColor = null;
          this.linearGradient = null;
          this.radialGradient = null;
        }
      }
  };

  jQuery.fn.jGraduateDefaults = {
    paint: new $.jGraduate.Paint(),
    window: {
      pickerTitle: "拖动标记以拾取绘制"
    },
    images: {
      clientPath: "images/"
    },
    newstop: 'inverse' // same, inverse, black, white
  };

  var isGecko = navigator.userAgent.indexOf('Gecko/') >= 0;

  function setAttrs(elem, attrs) {
    if (isGecko) {
      for (var aname in attrs) elem.setAttribute(aname, attrs[aname]);
    } else {
      for (var aname in attrs) {
        var val = attrs[aname], prop = elem[aname];
        if (prop && prop.constructor === 'SVGLength') {
          prop.baseVal.value = val;
        } else {
          elem.setAttribute(aname, val);
        }
      }
    }
  }

  function mkElem(name, attrs, newparent) {
    var elem = document.createElementNS(ns.svg, name);
    setAttrs(elem, attrs);
    if (newparent) newparent.appendChild(elem);
    return elem;
  }

  jQuery.fn.jGraduate =
    function (options) {
      var $arguments = arguments;
      return this.each(function () {
        var $this = $(this), $settings = $.extend(true, {}, jQuery.fn.jGraduateDefaults, options),
          id = $this.attr('id'),
          idref = '#' + $this.attr('id') + ' ';

        if (!idref) {
          alert('Container element must have an id attribute to maintain unique id strings for sub-elements.');
          return;
        }

        var okClicked = function () {
          switch ($this.paint.type) {
            case "radialGradient":
              $this.paint.linearGradient = null;
              break;
            case "linearGradient":
              $this.paint.radialGradient = null;
              break;
            case "solidColor":
              $this.paint.radialGradient = $this.paint.linearGradient = null;
              break;
          }
          $.isFunction($this.okCallback) && $this.okCallback($this.paint);
          $this.hide();
        },
          cancelClicked = function () {
            $.isFunction($this.cancelCallback) && $this.cancelCallback();
            $this.hide();
          };

        $.extend(true, $this, // public properties, methods, and callbacks
          {
            // make a copy of the incoming paint
            paint: new $.jGraduate.Paint({ copy: $settings.paint }),
            okCallback: $.isFunction($arguments[1]) && $arguments[1] || null,
            cancelCallback: $.isFunction($arguments[2]) && $arguments[2] || null
          });

        var pos = $this.position(),
          color = null;
        var $win = $(window);

        if ($this.paint.type == "none") {
          $this.paint = $.jGraduate.Paint({ solidColor: 'ffffff' });
        }

        $this.addClass('jGraduate_Picker');
        $this.html('<ul class="jGraduate_tabs">' +
          '<li class="jGraduate_tab_color jGraduate_tab_current" data-type="col">纯色</li>' +
          '<li class="jGraduate_tab_lingrad" data-type="lg">线性渐变</li>' +
          '<li class="jGraduate_tab_radgrad" data-type="rg">径向渐变</li>' +
          '</ul>' +
          '<div class="jGraduate_colPick"></div>' +
          '<div class="jGraduate_gradPick"></div>' +
          '<div class="jGraduate_LightBox"></div>' +
          '<div id="' + id + '_jGraduate_stopPicker" class="jGraduate_stopPicker"></div>'


        );
        var colPicker = $(idref + '> .jGraduate_colPick');
        var gradPicker = $(idref + '> .jGraduate_gradPick');

        gradPicker.html(
          '<div id="' + id + '_jGraduate_Swatch" class="jGraduate_Swatch">' +
          '<h2 class="jGraduate_Title">' + $settings.window.pickerTitle + '</h2>' +
          '<div id="' + id + '_jGraduate_GradContainer" class="jGraduate_GradContainer"></div>' +
          '<div id="' + id + '_jGraduate_StopSlider" class="jGraduate_StopSlider"></div>' +
          '</div>' +
          '<div class="jGraduate_Form jGraduate_Points jGraduate_lg_field">' +
          '<div class="jGraduate_StopSection">' +
          '<label class="jGraduate_Form_Heading">开始点</label>' +
          '<div class="jGraduate_Form_Section">' +
          '<label>x:</label>' +
          '<input type="text" id="' + id + '_jGraduate_x1" size="3" title="Enter starting x value between 0.0 and 1.0"/>' +
          '<label> y:</label>' +
          '<input type="text" id="' + id + '_jGraduate_y1" size="3" title="Enter starting y value between 0.0 and 1.0"/>' +
          '</div>' +
          '</div>' +
          '<div class="jGraduate_StopSection">' +
          '<label class="jGraduate_Form_Heading">结束点</label>' +
          '<div class="jGraduate_Form_Section">' +
          '<label>x:</label>' +
          '<input type="text" id="' + id + '_jGraduate_x2" size="3" title="Enter ending x value between 0.0 and 1.0"/>' +
          '<label> y:</label>' +
          '<input type="text" id="' + id + '_jGraduate_y2" size="3" title="Enter ending y value between 0.0 and 1.0"/>' +
          '</div>' +
          '</div>' +
          '</div>' +
          '<div class="jGraduate_Form jGraduate_Points jGraduate_rg_field">' +
          '<div class="jGraduate_StopSection">' +
          '<label class="jGraduate_Form_Heading">中心点</label>' +
          '<div class="jGraduate_Form_Section">' +
          '<label>x:</label>' +
          '<input type="text" id="' + id + '_jGraduate_cx" size="3" title="Enter x value between 0.0 and 1.0"/>' +
          '<label> y:</label>' +
          '<input type="text" id="' + id + '_jGraduate_cy" size="3" title="Enter y value between 0.0 and 1.0"/>' +
          '</div>' +
          '</div>' +
          '<div class="jGraduate_StopSection">' +
          '<label class="jGraduate_Form_Heading">焦点</label>' +
          '<div class="jGraduate_Form_Section">' +
          '<label>匹配中心点: <input type="checkbox" checked="checked" id="' + id + '_jGraduate_match_ctr"/></label><br/>' +
          '<label>x:</label>' +
          '<input type="text" id="' + id + '_jGraduate_fx" size="3" title="Enter x value between 0.0 and 1.0"/>' +
          '<label> y:</label>' +
          '<input type="text" id="' + id + '_jGraduate_fy" size="3" title="Enter y value between 0.0 and 1.0"/>' +
          '</div>' +
          '</div>' +
          '</div>' +
          '<div class="jGraduate_StopSection jGraduate_SpreadMethod">' +
          '<label class="jGraduate_Form_Heading">Spread method</label>' +
          '<div class="jGraduate_Form_Section">' +
          '<select class="jGraduate_spreadMethod">' +
          '<option value=pad selected>Pad</option>' +
          '<option value=reflect>Reflect</option>' +
          '<option value=repeat>Repeat</option>' +
          '</select>' +
          '</div>' +
          '</div>' +
          '<div class="jGraduate_Form">' +
          '<div class="jGraduate_Slider jGraduate_RadiusField jGraduate_rg_field">' +
          '<label class="prelabel">半径:</label>' +
          '<div id="' + id + '_jGraduate_Radius" class="jGraduate_SliderBar jGraduate_Radius" title="单击以设置半径">' +
          '<img id="' + id + '_jGraduate_RadiusArrows" class="jGraduate_RadiusArrows" src="' + $settings.images.clientPath + 'rangearrows2.svg" width="9" height="20">' +
          '</div>' +
          '<label><input type="text" id="' + id + '_jGraduate_RadiusInput" size="3" value="100"/>%</label>' +
          '</div>' +
          '<div class="jGraduate_Slider jGraduate_EllipField jGraduate_rg_field">' +
          '<label class="prelabel">椭圆度:</label>' +
          '<div id="' + id + '_jGraduate_Ellip" class="jGraduate_SliderBar jGraduate_Ellip" title="单击以设置椭圆度">' +
          '<img id="' + id + '_jGraduate_EllipArrows" class="jGraduate_EllipArrows" src="' + $settings.images.clientPath + 'rangearrows2.svg" width="9" height="20">' +
          '</div>' +
          '<label><input type="text" id="' + id + '_jGraduate_EllipInput" size="3" value="0"/>%</label>' +
          '</div>' +
          '<div class="jGraduate_Slider jGraduate_AngleField jGraduate_rg_field">' +
          '<label class="prelabel">角度:</label>' +
          '<div id="' + id + '_jGraduate_Angle" class="jGraduate_SliderBar jGraduate_Angle" title="单击以设置角度">' +
          '<img id="' + id + '_jGraduate_AngleArrows" class="jGraduate_AngleArrows" src="' + $settings.images.clientPath + 'rangearrows2.svg" width="9" height="20">' +
          '</div>' +
          '<label><input type="text" id="' + id + '_jGraduate_AngleInput" size="3" value="0"/>º&nbsp;</label>' +
          '</div>' +
          '<div class="jGraduate_Slider jGraduate_OpacField">' +
          '<label class="prelabel">透明度:</label>' +
          '<div id="' + id + '_jGraduate_Opac" class="jGraduate_SliderBar jGraduate_Opac" title="点击以设置透明度">' +
          '<img id="' + id + '_jGraduate_OpacArrows" class="jGraduate_OpacArrows" src="' + $settings.images.clientPath + 'rangearrows2.svg" width="9" height="20">' +
          '</div>' +
          '<label><input type="text" id="' + id + '_jGraduate_OpacInput" size="3" value="100"/>%</label>' +
          '</div>' +
          '</div>' +
          '<div class="jGraduate_OkCancel">' +
          '<input type="button" id="' + id + '_jGraduate_Ok" class="jGraduate_Ok" value="确定"/>' +
          '<input type="button" id="' + id + '_jGraduate_Cancel" class="jGraduate_Cancel" value="取消"/>' +
          '</div>');

        // --------------
        // Set up all the SVG elements (the gradient, stops and rectangle)
        var MAX = 256, MARGINX = 0, MARGINY = 0, STOP_RADIUS = 15 / 2,
          SIZEX = MAX - 2 * MARGINX, SIZEY = MAX - 2 * MARGINY;

        var curType, curGradient, previewRect;

        var attr_input = {};

        var SLIDERW = 145;
        $('.jGraduate_SliderBar').width(SLIDERW);

        var container = $('#' + id + '_jGraduate_GradContainer')[0];

        var svg = mkElem('svg', {
          id: id + '_jgraduate_svg',
          width: MAX,
          height: MAX,
          xmlns: ns.svg,
          //shuaihu
          fill: "#2f2f2c"
        }, container);

        // if we are sent a gradient, import it 

        curType = curType || $this.paint.type;

        var grad = curGradient = $this.paint[curType];

        var gradalpha = $this.paint.alpha;

        var isSolid = curType === 'solidColor';

        // Make any missing gradients
        switch (curType) {
          case "solidColor":
          // fall through
          case "linearGradient":
            if (!isSolid) {
              curGradient.id = id + '_lg_jgraduate_grad';
              grad = curGradient = svg.appendChild(curGradient);//.cloneNode(true));
            }
            mkElem('radialGradient', {
              id: id + '_rg_jgraduate_grad'
            }, svg);
            if (curType === "linearGradient") break;
          case "radialGradient":
            if (!isSolid) {
              curGradient.id = id + '_rg_jgraduate_grad';
              grad = curGradient = svg.appendChild(curGradient);//.cloneNode(true));
            }
            mkElem('linearGradient', {
              id: id + '_lg_jgraduate_grad',
              x1: 0,
              y1: 0,
              x2: 1,
              y2: 0,
            }, svg);
        }

        if (isSolid) {
          grad = curGradient = $('#' + id + '_lg_jgraduate_grad')[0];
          var color = $this.paint[curType];
          mkStop(0, '#' + color, 1);

          var type = typeof $settings.newstop;

          if (type === 'string') {
            switch ($settings.newstop) {
              case 'same':
                mkStop(1, '#' + color, 1);
                break;

              case 'inverse':
                // Invert current color for second stop
                var inverted = '';
                if (color.length === 3) {
                  color = color.split("").map(function (d) { return d + "" + d }).join("");
                }
                for (var i = 0; i < 6; i += 2) {
                  var ch = color.substr(i, 2);
                  var inv = (255 - parseInt(color.substr(i, 2), 16)).toString(16);
                  if (inv.length < 2) inv = 0 + inv;
                  inverted += inv;
                }
                mkStop(1, '#' + inverted, 1);
                break;

              case 'white':
                mkStop(1, '#ffffff', 1);
                break;

              case 'black':
                mkStop(1, '#000000', 1);
                break;
            }
          } else if (type === 'object') {
            var opac = ('opac' in $settings.newstop) ? $settings.newstop.opac : 1;
            mkStop(1, ($settings.newstop.color || '#' + color), opac);
          }
        }


        var x1 = parseFloat(grad.getAttribute('x1') || 0.0),
          y1 = parseFloat(grad.getAttribute('y1') || 0.0),
          x2 = parseFloat(grad.getAttribute('x2') || 1.0),
          y2 = parseFloat(grad.getAttribute('y2') || 0.0);

        var cx = parseFloat(grad.getAttribute('cx') || 0.5),
          cy = parseFloat(grad.getAttribute('cy') || 0.5),
          fx = parseFloat(grad.getAttribute('fx') || cx),
          fy = parseFloat(grad.getAttribute('fy') || cy);


        var previewRect = mkElem('rect', {
          id: id + '_jgraduate_rect',
          x: MARGINX,
          y: MARGINY,
          width: SIZEX,
          height: SIZEY,
          fill: 'url(#' + id + '_jgraduate_grad)',
          'fill-opacity': gradalpha / 100
        }, svg);

        // stop visuals created here
        var beginCoord = $('<div/>').attr({
          'class': 'grad_coord jGraduate_lg_field',
          title: 'Begin Stop'
        }).text(1).css({
          top: y1 * MAX,
          left: x1 * MAX
        }).data('coord', 'start').appendTo(container);

        var endCoord = beginCoord.clone().text(2).css({
          top: y2 * MAX,
          left: x2 * MAX
        }).attr('title', 'End stop').data('coord', 'end').appendTo(container);

        var centerCoord = $('<div/>').attr({
          'class': 'grad_coord jGraduate_rg_field',
          title: 'Center stop'
        }).text('C').css({
          top: cy * MAX,
          left: cx * MAX
        }).data('coord', 'center').appendTo(container);

        var focusCoord = centerCoord.clone().text('F').css({
          top: fy * MAX,
          left: fx * MAX,
          display: 'none'
        }).attr('title', 'Focus point').data('coord', 'focus').appendTo(container);

        focusCoord[0].id = id + '_jGraduate_focusCoord';

        var coords = $(idref + ' .grad_coord');

        $.each(['x1', 'y1', 'x2', 'y2', 'cx', 'cy', 'fx', 'fy'], function (i, attr) {
          var attrval = curGradient.getAttribute(attr);

          var isRadial = isNaN(attr[1]);

          if (!attrval) {
            // Set defaults
            if (isRadial) {
              // For radial points
              attrval = "0.5";
            } else {
              // Only x2 is 1
              attrval = attr === 'x2' ? "1.0" : "0.0";
            }
          }

          attr_input[attr] = $('#' + id + '_jGraduate_' + attr)
            .val(attrval)
            .change(function () {
              // TODO: Support values < 0 and > 1 (zoomable preview?)
              if (isNaN(parseFloat(this.value)) || this.value < 0) {
                this.value = 0.0;
              } else if (this.value > 1) {
                this.value = 1.0;
              }

              if (!(attr[0] === 'f' && !showFocus)) {
                if (isRadial && curType === 'radialGradient' || !isRadial && curType === 'linearGradient') {
                  curGradient.setAttribute(attr, this.value);
                }
              }

              if (isRadial) {
                var $elem = attr[0] === "c" ? centerCoord : focusCoord;
              } else {
                var $elem = attr[1] === "1" ? beginCoord : endCoord;
              }

              var cssName = attr.indexOf('x') >= 0 ? 'left' : 'top';

              $elem.css(cssName, this.value * MAX);
            }).change();
        });

        function mkStop(n, color, opac, sel, stop_elem) {
          var stop = stop_elem || mkElem('stop', { 'stop-color': color, 'stop-opacity': opac, offset: n }, curGradient);
          if (stop_elem) {
            color = stop_elem.getAttribute('stop-color');
            opac = stop_elem.getAttribute('stop-opacity');
            n = stop_elem.getAttribute('offset');
          } else {
            curGradient.appendChild(stop);
          }
          if (opac === null) opac = 1;

          var picker_d = 'M-6.2,0.9c3.6-4,6.7-4.3,6.7-12.4c-0.2,7.9,3.1,8.8,6.5,12.4c3.5,3.8,2.9,9.6,0,12.3c-3.1,2.8-10.4,2.7-13.2,0C-9.6,9.9-9.4,4.4-6.2,0.9z';

          var pathbg = mkElem('path', {
            d: picker_d,
            fill: 'url(#jGraduate_trans)',
            transform: 'translate(' + (10 + n * MAX) + ', 26)'
          }, stopGroup);

          var path = mkElem('path', {
            d: picker_d,
            fill: color,
            'fill-opacity': opac,
            transform: 'translate(' + (10 + n * MAX) + ', 26)',
            stroke: '#000',
            'stroke-width': 1.5
          }, stopGroup);

          $(path).mousedown(function (e) {
            selectStop(this);
            drag = cur_stop;
            $win.mousemove(dragColor).mouseup(remDrags);
            stop_offset = stopMakerDiv.offset();
            return true;
          }).data('stop', stop).data('bg', pathbg).dblclick(function () {
            $('div.jGraduate_LightBox').show();
            var colorhandle = this;
            var stopOpacity = +stop.getAttribute('stop-opacity') || 1;
            var stopColor = stop.getAttribute('stop-color') || 1;
            var thisAlpha = (parseFloat(stopOpacity) * 255).toString(16);
            while (thisAlpha.length < 2) { thisAlpha = "0" + thisAlpha; }
            color = stopColor.substr(1) + thisAlpha;
            $('#' + id + '_jGraduate_stopPicker').css({ 'left': 100, 'bottom': 15 }).jPicker({
              window: { title: "Pick the start color and opacity for the gradient" },
              images: { clientPath: $settings.images.clientPath },
              color: { active: color, alphaSupport: true }
            },
              function (color, arg2) {
                stopColor = color.val('hex') ? ('#' + color.val('hex')) : "none";
                stopOpacity = color.val('a') !== null ? color.val('a') / 256 : 1;
                colorhandle.setAttribute('fill', stopColor);
                colorhandle.setAttribute('fill-opacity', stopOpacity);
                stop.setAttribute('stop-color', stopColor);
                stop.setAttribute('stop-opacity', stopOpacity);
                $('div.jGraduate_LightBox').hide();
                $('#' + id + '_jGraduate_stopPicker').hide();
              }, null, function () {
                $('div.jGraduate_LightBox').hide();
                $('#' + id + '_jGraduate_stopPicker').hide();
              });
          });

          $(curGradient).find('stop').each(function () {
            var cur_s = $(this);
            if (+this.getAttribute('offset') > n) {
              if (!color) {
                var newcolor = this.getAttribute('stop-color');
                var newopac = this.getAttribute('stop-opacity');
                stop.setAttribute('stop-color', newcolor);
                path.setAttribute('fill', newcolor);
                stop.setAttribute('stop-opacity', newopac === null ? 1 : newopac);
                path.setAttribute('fill-opacity', newopac === null ? 1 : newopac);
              }
              cur_s.before(stop);
              return false;
            }
          });
          if (sel) selectStop(path);
          return stop;
        }

        function remStop() {
          delStop.setAttribute('display', 'none');
          var path = $(cur_stop);
          var stop = path.data('stop');
          var bg = path.data('bg');
          $([cur_stop, stop, bg]).remove();
        }


        var stops, stopGroup;

        var stopMakerDiv = $('#' + id + '_jGraduate_StopSlider');

        var cur_stop, stopGroup, stopMakerSVG, drag;

        var delStop = mkElem('path', {
          d: 'm9.75,-6l-19.5,19.5m0,-19.5l19.5,19.5',
          fill: 'none',
          stroke: '#D00',
          'stroke-width': 5,
          display: 'none'
        }, stopMakerSVG);


        function selectStop(item) {
          if (cur_stop) cur_stop.setAttribute('stroke', '#000');
          item.setAttribute('stroke', 'blue');
          cur_stop = item;
          //  cur_stop.parentNode.appendChild(cur_stop);
          //  stops = $('stop');
          //  opac_select.val(cur_stop.attr('fill-opacity') || 1);
          //  root.append(delStop);
        }

        var stop_offset;

        function remDrags() {
          $win.unbind('mousemove', dragColor);
          if (delStop.getAttribute('display') !== 'none') {
            remStop();
          }
          drag = null;
        }

        var scale_x = 1, scale_y = 1, angle = 0;
        var c_x = cx;
        var c_y = cy;

        function xform() {
          var rot = angle ? 'rotate(' + angle + ',' + c_x + ',' + c_y + ') ' : '';
          if (scale_x === 1 && scale_y === 1) {
            curGradient.removeAttribute('gradientTransform');
            //          $('#ang').addClass('dis');
          } else {
            var x = -c_x * (scale_x - 1);
            var y = -c_y * (scale_y - 1);
            curGradient.setAttribute('gradientTransform', rot + 'translate(' + x + ',' + y + ') scale(' + scale_x + ',' + scale_y + ')');
            //          $('#ang').removeClass('dis');
          }
        }

        function dragColor(evt) {

          var x = evt.pageX - stop_offset.left;
          var y = evt.pageY - stop_offset.top;
          x = x < 10 ? 10 : x > MAX + 10 ? MAX + 10 : x;

          var xf_str = 'translate(' + x + ', 26)';
          if (y < -60 || y > 130) {
            delStop.setAttribute('display', 'block');
            delStop.setAttribute('transform', xf_str);
          } else {
            delStop.setAttribute('display', 'none');
          }

          drag.setAttribute('transform', xf_str);
          $.data(drag, 'bg').setAttribute('transform', xf_str);
          var stop = $.data(drag, 'stop');
          var s_x = (x - 10) / MAX;

          stop.setAttribute('offset', s_x);
          var last = 0;

          $(curGradient).find('stop').each(function (i) {
            var cur = this.getAttribute('offset');
            var t = $(this);
            if (cur < last) {
              t.prev().before(t);
              stops = $(curGradient).find('stop');
            }
            last = cur;
          });

        }

        stopMakerSVG = mkElem('svg', {
          width: '100%',
          height: 45,
          //shuaihu
          fill: "#2f2f2c"
        }, stopMakerDiv[0]);

        var trans_pattern = mkElem('pattern', {
          width: 16,
          height: 16,
          patternUnits: 'userSpaceOnUse',
          id: 'jGraduate_trans'
        }, stopMakerSVG);

        var trans_img = mkElem('image', {
          width: 16,
          height: 16
        }, trans_pattern);

        var bg_image = $settings.images.clientPath + 'map-opacity.png';

        trans_img.setAttributeNS(ns.xlink, 'xlink:href', bg_image);

        $(stopMakerSVG).on("click touchstart", function (evt) {
          stop_offset = stopMakerDiv.offset();
          var target = evt.target;
          if (target.tagName === 'path') return;
          var x = evt.pageX - stop_offset.left - 8;
          x = x < 10 ? 10 : x > MAX + 10 ? MAX + 10 : x;
          mkStop(x / MAX, 0, 0, true);
          evt.stopPropagation();
        });

        $(stopMakerSVG).mouseover(function () {
          stopMakerSVG.appendChild(delStop);
        });

        stopGroup = mkElem('g', {}, stopMakerSVG);

        mkElem('line', {
          x1: 10,
          y1: 15,
          x2: MAX + 10,
          y2: 15,
          'stroke-width': 2,
          //stroke: '#000' shuaihu
          stroke: "#4f80ff"
        }, stopMakerSVG);


        var spreadMethodOpt = gradPicker.find('.jGraduate_spreadMethod').change(function () {
          curGradient.setAttribute('spreadMethod', $(this).val());
        });


        // handle dragging the stop around the swatch
        var draggingCoord = null;

        var onCoordDrag = function (evt) {
          var x = evt.pageX - offset.left;
          var y = evt.pageY - offset.top;

          // clamp stop to the swatch
          x = x < 0 ? 0 : x > MAX ? MAX : x;
          y = y < 0 ? 0 : y > MAX ? MAX : y;

          draggingCoord.css('left', x).css('top', y);

          // calculate stop offset                
          var fracx = x / SIZEX;
          var fracy = y / SIZEY;

          var type = draggingCoord.data('coord');
          var grad = curGradient;

          switch (type) {
            case 'start':
              attr_input.x1.val(fracx);
              attr_input.y1.val(fracy);
              grad.setAttribute('x1', fracx);
              grad.setAttribute('y1', fracy);
              break;
            case 'end':
              attr_input.x2.val(fracx);
              attr_input.y2.val(fracy);
              grad.setAttribute('x2', fracx);
              grad.setAttribute('y2', fracy);
              break;
            case 'center':
              attr_input.cx.val(fracx);
              attr_input.cy.val(fracy);
              grad.setAttribute('cx', fracx);
              grad.setAttribute('cy', fracy);
              c_x = fracx;
              c_y = fracy;
              xform();
              break;
            case 'focus':
              attr_input.fx.val(fracx);
              attr_input.fy.val(fracy);
              grad.setAttribute('fx', fracx);
              grad.setAttribute('fy', fracy);
              xform();
          }

          evt.preventDefault();
        }

        var onCoordUp = function () {
          draggingCoord = null;
          $win.unbind('mousemove', onCoordDrag).unbind('mouseup', onCoordUp);
        }

        // Linear gradient
        //      (function() {


        stops = curGradient.getElementsByTagNameNS(ns.svg, 'stop');

        // if there are not at least two stops, then 
        if (numstops < 2) {
          while (numstops < 2) {
            curGradient.appendChild(document.createElementNS(ns.svg, 'stop'));
            ++numstops;
          }
          stops = curGradient.getElementsByTagNameNS(ns.svg, 'stop');
        }

        var numstops = stops.length;
        for (var i = 0; i < numstops; i++) {
          mkStop(0, 0, 0, 0, stops[i]);
        }

        spreadMethodOpt.val(curGradient.getAttribute('spreadMethod') || 'pad');

        var offset;

        // No match, so show focus point
        var showFocus = false;

        previewRect.setAttribute('fill-opacity', gradalpha / 100);


        $('#' + id + ' div.grad_coord').mousedown(function (evt) {
          evt.preventDefault();
          draggingCoord = $(this);
          var s_pos = draggingCoord.offset();
          offset = draggingCoord.parent().offset();
          $win.mousemove(onCoordDrag).mouseup(onCoordUp);
        });

        // bind GUI elements
        $('#' + id + '_jGraduate_Ok').bind('click touchstart', function () {
          $this.paint.type = curType;
          $this.paint[curType] = curGradient.cloneNode(true);;
          $this.paint.solidColor = null;
          okClicked();
        });
        $('#' + id + '_jGraduate_Cancel').bind('click touchstart', function (paint) {
          cancelClicked();
        });

        if (curType === 'radialGradient') {
          if (showFocus) {
            focusCoord.show();
          } else {
            focusCoord.hide();
            attr_input.fx.val("");
            attr_input.fy.val("");
          }
        }

        $("#" + id + "_jGraduate_match_ctr")[0].checked = !showFocus;

        var lastfx, lastfy;

        $("#" + id + "_jGraduate_match_ctr").change(function () {
          showFocus = !this.checked;
          focusCoord.toggle(showFocus);
          attr_input.fx.val('');
          attr_input.fy.val('');
          var grad = curGradient;
          if (!showFocus) {
            lastfx = grad.getAttribute('fx');
            lastfy = grad.getAttribute('fy');
            grad.removeAttribute('fx');
            grad.removeAttribute('fy');
          } else {
            var fx = lastfx || .5;
            var fy = lastfy || .5;
            grad.setAttribute('fx', fx);
            grad.setAttribute('fy', fy);
            attr_input.fx.val(fx);
            attr_input.fy.val(fy);
          }
        });

        var stops = curGradient.getElementsByTagNameNS(ns.svg, 'stop');
        var numstops = stops.length;
        // if there are not at least two stops, then 
        if (numstops < 2) {
          while (numstops < 2) {
            curGradient.appendChild(document.createElementNS(ns.svg, 'stop'));
            ++numstops;
          }
          stops = curGradient.getElementsByTagNameNS(ns.svg, 'stop');
        }

        var slider;

        var setSlider = function (e) {
          var offset = slider.offset;
          var div = slider.parent;
          var x = (e.pageX - offset.left - parseInt(div.css('border-left-width')));
          if (x > SLIDERW) x = SLIDERW;
          if (x <= 0) x = 0;
          var posx = x - 5;
          x /= SLIDERW;

          switch (slider.type) {
            case 'radius':
              x = Math.pow(x * 2, 2.5);
              if (x > .98 && x < 1.02) x = 1;
              if (x <= .01) x = .01;
              curGradient.setAttribute('r', x);
              break;
            case 'opacity':
              $this.paint.alpha = parseInt(x * 100);
              previewRect.setAttribute('fill-opacity', x);
              break;
            case 'ellip':
              scale_x = 1, scale_y = 1;
              if (x < .5) {
                x /= .5; // 0.001
                scale_x = x <= 0 ? .01 : x;
              } else if (x > .5) {
                x /= .5; // 2
                x = 2 - x;
                scale_y = x <= 0 ? .01 : x;
              }
              xform();
              x -= 1;
              if (scale_y === x + 1) {
                x = Math.abs(x);
              }
              break;
            case 'angle':
              x = x - .5;
              angle = x *= 180;
              xform();
              x /= 100;
              break;
          }
          slider.elem.css({ 'margin-left': posx });
          x = Math.round(x * 100);
          slider.input.val(x);
        };

        var ellip_val = 0, angle_val = 0;

        if (curType === 'radialGradient') {
          var tlist = curGradient.gradientTransform.baseVal;
          if (tlist.numberOfItems === 2) {
            var t = tlist.getItem(0);
            var s = tlist.getItem(1);
            if (t.type === 2 && s.type === 3) {
              var m = s.matrix;
              if (m.a !== 1) {
                ellip_val = Math.round(-(1 - m.a) * 100);
              } else if (m.d !== 1) {
                ellip_val = Math.round((1 - m.d) * 100);
              }
            }
          } else if (tlist.numberOfItems === 3) {
            // Assume [R][T][S]
            var r = tlist.getItem(0);
            var t = tlist.getItem(1);
            var s = tlist.getItem(2);

            if (r.type === 4
              && t.type === 2
              && s.type === 3) {

              angle_val = Math.round(r.angle);
              var m = s.matrix;
              if (m.a !== 1) {
                ellip_val = Math.round(-(1 - m.a) * 100);
              } else if (m.d !== 1) {
                ellip_val = Math.round((1 - m.d) * 100);
              }

            }
          }
        }

        var sliders = {
          radius: {
            handle: '#' + id + '_jGraduate_RadiusArrows',
            input: '#' + id + '_jGraduate_RadiusInput',
            val: (curGradient.getAttribute('r') || .5) * 100
          },
          opacity: {
            handle: '#' + id + '_jGraduate_OpacArrows',
            input: '#' + id + '_jGraduate_OpacInput',
            val: $this.paint.alpha || 100
          },
          ellip: {
            handle: '#' + id + '_jGraduate_EllipArrows',
            input: '#' + id + '_jGraduate_EllipInput',
            val: ellip_val
          },
          angle: {
            handle: '#' + id + '_jGraduate_AngleArrows',
            input: '#' + id + '_jGraduate_AngleInput',
            val: angle_val
          }
        }

        $.each(sliders, function (type, data) {
          var handle = $(data.handle);
          handle.mousedown(function (evt) {
            var parent = handle.parent();
            slider = {
              type: type,
              elem: handle,
              input: $(data.input),
              parent: parent,
              offset: parent.offset()
            };
            $win.mousemove(dragSlider).mouseup(stopSlider);
            evt.preventDefault();
          });

          $(data.input).val(data.val).change(function () {
            var val = +this.value;
            var xpos = 0;
            var isRad = curType === 'radialGradient';
            switch (type) {
              case 'radius':
                if (isRad) curGradient.setAttribute('r', val / 100);
                xpos = (Math.pow(val / 100, 1 / 2.5) / 2) * SLIDERW;
                break;

              case 'opacity':
                $this.paint.alpha = val;
                previewRect.setAttribute('fill-opacity', val / 100);
                xpos = val * (SLIDERW / 100);
                break;

              case 'ellip':
                scale_x = scale_y = 1;
                if (val === 0) {
                  xpos = SLIDERW * .5;
                  break;
                }
                if (val > 99.5) val = 99.5;
                if (val > 0) {
                  scale_y = 1 - (val / 100);
                } else {
                  scale_x = - (val / 100) - 1;
                }

                xpos = SLIDERW * ((val + 100) / 2) / 100;
                if (isRad) xform();
                break;

              case 'angle':
                angle = val;
                xpos = angle / 180;
                xpos += .5;
                xpos *= SLIDERW;
                if (isRad) xform();
            }
            if (xpos > SLIDERW) {
              xpos = SLIDERW;
            } else if (xpos < 0) {
              xpos = 0;
            }
            handle.css({ 'margin-left': xpos - 5 });
          }).change();
        });

        var dragSlider = function (evt) {
          setSlider(evt);
          evt.preventDefault();
        };

        var stopSlider = function (evt) {
          $win.unbind('mousemove', dragSlider).unbind('mouseup', stopSlider);
          slider = null;
        };


        // --------------
        var thisAlpha = ($this.paint.alpha * 255 / 100).toString(16);
        while (thisAlpha.length < 2) { thisAlpha = "0" + thisAlpha; }
        thisAlpha = thisAlpha.split(".")[0];
        color = $this.paint.solidColor == "none" ? "" : $this.paint.solidColor + thisAlpha;

        if (!isSolid) {
          color = stops[0].getAttribute('stop-color');
        }

        // This should be done somewhere else, probably
        $.extend($.fn.jPicker.defaults.window, {
          alphaSupport: true, effects: { type: 'show', speed: 0 }
        });

        colPicker.jPicker(
          {
            window: { title: $settings.window.pickerTitle },
            images: { clientPath: $settings.images.clientPath },
            color: { active: color, alphaSupport: true }
          },
          function (color) {
            $this.paint.type = "solidColor";
            $this.paint.alpha = color.val('ahex') ? Math.round((color.val('a') / 255) * 100) : 100;
            $this.paint.solidColor = color.val('hex') ? color.val('hex') : "none";
            $this.paint.radialGradient = null;
            okClicked();
          },
          null,
          function () { cancelClicked(); }
        );


        var tabs = $(idref + ' .jGraduate_tabs li');
        tabs.on("click touchstart", function () {
          tabs.removeClass('jGraduate_tab_current');
          $(this).addClass('jGraduate_tab_current');
          $(idref + " > div").hide();
          var type = $(this).attr('data-type');
          var container = $(idref + ' .jGraduate_gradPick').show();
          if (type === 'rg' || type === 'lg') {
            // Show/hide appropriate fields
            $('.jGraduate_' + type + '_field').show();
            $('.jGraduate_' + (type === 'lg' ? 'rg' : 'lg') + '_field').hide();

            $('#' + id + '_jgraduate_rect')[0].setAttribute('fill', 'url(#' + id + '_' + type + '_jgraduate_grad)');

            // Copy stops

            curType = type === 'lg' ? 'linearGradient' : 'radialGradient';

            $('#' + id + '_jGraduate_OpacInput').val($this.paint.alpha).change();

            var newGrad = $('#' + id + '_' + type + '_jgraduate_grad')[0];

            if (curGradient !== newGrad) {
              var cur_stops = $(curGradient).find('stop');
              $(newGrad).empty().append(cur_stops);
              curGradient = newGrad;
              var sm = spreadMethodOpt.val();
              curGradient.setAttribute('spreadMethod', sm);
            }
            showFocus = type === 'rg' && curGradient.getAttribute('fx') != null && !(cx == fx && cy == fy);
            $('#' + id + '_jGraduate_focusCoord').toggle(showFocus);
            if (showFocus) {
              $('#' + id + '_jGraduate_match_ctr')[0].checked = false;
            }
          } else {
            $(idref + ' .jGraduate_gradPick').hide();
            $(idref + ' .jGraduate_colPick').show();
          }
        });
        $(idref + " > div").hide();
        tabs.removeClass('jGraduate_tab_current');
        var tab;
        switch ($this.paint.type) {
          case 'linearGradient':
            tab = $(idref + ' .jGraduate_tab_lingrad');
            break;
          case 'radialGradient':
            tab = $(idref + ' .jGraduate_tab_radgrad');
            break;
          default:
            tab = $(idref + ' .jGraduate_tab_color');
            break;
        }
        $this.show();

        // jPicker will try to show after a 0ms timeout, so need to fire this after that
        setTimeout(function () {
          tab.addClass('jGraduate_tab_current').click();
        }, 10);
      });
    };
})();